﻿#region 版权声明
/**
 * 版权声明：LumaQQ.NET是基于LumaQQ分析的QQ协议，将其部分代码进行修改和翻译为.NET版本，并且继续使用LumaQQ的开源协议。
 * 本人没有对其核心协议进行改动， 也没有与腾讯公司的QQ软件有直接联系，请尊重LumaQQ作者Luma的著作权和版权声明。
 * 同时在使用此开发包前请自行协调好多方面关系，本人不享受和承担由此产生的任何权利以及任何法律责任。
 * 
 * 作者：阿不
 * 博客：http://hjf1223.cnblogs.com
 * Email：hjf1223 AT gmail.com
 * LumaQQ：http://lumaqq.linuxsir.org 
 * LumaQQ - Java QQ Client
 * 
 * Copyright (C) 2004 luma <stubma@163.com>
 * 
 * LumaQQ - For .NET QQClient
 * Copyright (C) 2008 阿不<hjf1223 AT gmail.com>
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 * 
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
 */
#endregion

using System;
using System.Collections.Generic;
using System.Text;

using LumaQQ.NET.Net;
using LumaQQ.NET.Events;
using LumaQQ.NET.Packets;
using LumaQQ.NET.Entities;
using LumaQQ.NET.Threading;
using LumaQQ.NET.Packets.In;
using LumaQQ.NET.Packets.Out;
namespace LumaQQ.NET
{
    /// <summary>群功能
    /// 得到我加入的所有群,需要通过FriendManager.DownloadGroupFriends来得到
    /// 在返回的数据包当中,有标识该好友是普通好友,还是群好友.
    /// 	<remark>abu 2008-03-29 </remark>
    /// </summary>
    public class ClusterManager
    {
        /// <summary>
        /// 	<remark>abu 2008-03-07 </remark>
        /// </summary>
        /// <value></value>
        public QQClient QQClient { get; private set; }
        /// <summary>
        /// 	<remark>abu 2008-03-29 </remark>
        /// </summary>
        /// <value></value>
        public QQUser QQUser { get { return QQClient.QQUser; } }
        /// <summary>
        /// 	<remark>abu 2008-03-29 </remark>
        /// </summary>
        /// <param name="client">The client.</param>
        public ClusterManager(QQClient client)
        {
            QQClient = client;
        }

        /// <summary>加入群
        /// Joins the cluster.
        /// </summary>
        /// <param name="clusterId">The cluster id.</param>
        public void JoinCluster(int clusterId)
        {
            ClusterJoinPacket packet = new ClusterJoinPacket(QQUser);
            packet.ClusterId = clusterId;
            QQClient.PacketManager.SendPacket(packet, QQPort.Main.Name);
        }
        /// <summary>发送加入群请求信息
        /// Requests the join cluster.
        /// </summary>
        /// <param name="clusterId">The cluster id.</param>
        /// <param name="message">The message.</param>
        public void RequestJoinCluster(int clusterId, string message)
        {
            ClusterAuthPacket packet = new ClusterAuthPacket(QQUser);
            packet.Type = _08QQ.ClusterAuth.QQ_CLUSTER_AUTH_REQUEST;
            packet.ClusterId = clusterId;
            packet.Message = message;
            QQClient.PacketManager.SendPacket(packet, QQPort.Main.Name);
        }
        /// <summary>查找群
        /// Searches the cluster by id.
        /// </summary>
        /// <param name="externalId">The external id.</param>
        public void SearchClusterById(int externalId)
        {
            ClusterSearchPacket packet = new ClusterSearchPacket(QQUser);
            packet.ExternalId = externalId;
            packet.SearchType = _08QQ.Cluster_SearchType.QQ_CLUSTER_SEARCH_BY_ID;
            QQClient.PacketManager.SendPacket(packet, QQPort.Main.Name);
        }
        /// <summary>退出群
        /// Exits the cluster.
        /// </summary>
        /// <param name="externalId">The external id.</param>
        public void ExitCluster(int externalId)
        {
            ClusterExitPacket packet = new ClusterExitPacket(QQUser);
            packet.ClusterId = externalId;
            QQClient.PacketManager.SendPacket(packet, QQPort.Main.Name);
        }
        /// <summary>
        /// Sends the cluster IM.
        /// </summary>
        /// <param name="clusterId">The cluster id.</param>
        /// <param name="message">The message.</param>
        public void SendClusterIM(int clusterId, string message)
        {
            SendClusterIM(clusterId, message, 1, 0);
        }
        /// <summary>发送群信息
        /// Sends the cluster IM.
        /// </summary>
        /// <param name="clusterId">The cluster id.</param>
        /// <param name="message">The message.</param>
        /// <param name="totalFragments">The total fragments.</param>
        /// <param name="fragmentSequence">The fragment sequence.</param>
        public void SendClusterIM(int clusterId, string message, int totalFragments, int fragmentSequence)
        {
            SendClusterIM(clusterId, message, totalFragments, fragmentSequence, new FontStyle());
        }
        /// <summary>发送群信息
        /// Sends the cluster IM.
        /// </summary>
        /// <param name="clusterId">The cluster id.</param>
        /// <param name="message">The message.</param>
        /// <param name="totalFragments">The total fragments.</param>
        /// <param name="fragmentSequence">The fragment sequence.</param>
        /// <param name="fontStyle">The font style.</param>
        public void SendClusterIM(int clusterId, string message, int totalFragments, int fragmentSequence, FontStyle fontStyle)
        {
            ClusterSendIMExPacket packet = new ClusterSendIMExPacket(QQUser);
            packet.ClusterId = clusterId;
            packet.Message = message;
            packet.TotalFragments = totalFragments;
            packet.FragmentSequence = fragmentSequence;
            packet.FontStyle = fontStyle;
            QQClient.PacketManager.SendPacket(packet, QQPort.Main.Name);
        }
        /// <summary>请求群信息
        /// <remarks>
        /// 返回的ClusterCommandReplyPacket字段包括:
        /// Info
        /// Members
        /// </remarks>
        /// Gets the cluster info.
        /// </summary>
        /// <param name="clusterId">The cluster id.群内部ID</param>
        public void GetClusterInfo(int clusterId)
        {
            ClusterGetInfoPacket packet = new ClusterGetInfoPacket(QQUser);
            packet.ClusterId = clusterId;
            QQClient.PacketManager.SendPacket(packet, QQPort.Main.Name);
        }

        /// <summary>得到群中成员的信息
        /// Gets the cluster member info.
        /// <remarks>
        /// 返回的ClusterCommandReplyPacket字段包括:
        /// MemberInfos
        /// </remarks>
        /// </summary>
        /// <param name="clusterId">The cluster id.群的内部ID</param>
        /// <param name="members">The members.成员的QQ号列表，元素类型是Integer或者Member</param>
        public void GetClusterMemberInfo(int clusterId, int[] members)
        {
            // 由于一次最多只能得到61个成员的信息，所以这里按照30个成员一组进行拆分
            // 因为QQ是一次拆这么多
            int times = (members.Length + 29) / 30;
            for (int i = 0; i < times; i++)
            {
                ClusterGetMemberInfoPacket packet = new ClusterGetMemberInfoPacket(QQUser);
                packet.ClusterId = clusterId;
                for (int j = 30 * i; j < 30 * i + 30 && j < members.Length; j++)
                {
                    packet.Members.Add(members[j]);
                }
                QQClient.PacketManager.SendPacket(packet, QQPort.Main.Name);
            }
        }
        /// <summary>修改群名片
        /// Modifies the card.
        /// </summary>
        /// <param name="clusterId">The cluster id.</param>
        /// <param name="card">The card.</param>
        public void ModifyCard(int clusterId, Card card)
        {
            ClusterModifyCardPacket packet = new ClusterModifyCardPacket(QQUser);
            packet.ClusterId = clusterId;
            packet.Card = card;
            QQClient.PacketManager.SendPacket(packet, QQPort.Main.Name);
        }
        /// <summary>更新群组织结构
        /// Updates the organization.
        /// </summary>
        /// <param name="clusterId">The cluster id.</param>
        public void UpdateOrganization(int clusterId)
        {
            ClusterUpdateOrganizationPacket packet = new ClusterUpdateOrganizationPacket(QQUser);
            packet.ClusterId = clusterId;
            QQClient.PacketManager.SendPacket(packet, QQPort.Main.Name);
        }

        /// <summary>获取群在线成员
        /// Gets the cluster online member.
        /// </summary>
        /// <param name="clusterId">The cluster id.</param>
        public void GetClusterOnlineMember(int clusterId)
        {
            ClusterGetOnlineMemberPacket packet = new ClusterGetOnlineMemberPacket(QQUser);
            packet.ClusterId = clusterId;
            QQClient.PacketManager.SendPacket(packet, QQPort.Main.Name);
        }

        /// <summary>得到群成员名片
        /// Gets the card.
        /// </summary>
        /// <param name="clusterId">The cluster id.</param>
        /// <param name="qq">The qq.</param>
        public void GetCard(int clusterId, int qq)
        {
            ClusterGetCardPacket packet = new ClusterGetCardPacket(QQUser);
            packet.ClusterId = clusterId;
            packet.QQ = qq;
            QQClient.PacketManager.SendPacket(packet, QQPort.Main.Name);
        }
        /// <summary>成批得到群成员名片
        /// Gets the card batch.
        /// <remarks>注意,当ClusterCommandReplyPacket的NextStart值为0,表示已经得到所有的群成员名片,否则它的值就是下一个成员的开始下标
        /// </remarks>
        /// </summary>
        /// <param name="clusterId">The cluster id.</param>
        /// <param name="start">The start.</param>
        public void GetCardBatch(int clusterId, int start)
        {
            ClusterGetCardBatchPacket packet = new ClusterGetCardBatchPacket(QQUser);
            packet.ClusterId = clusterId;
            packet.Start = start;
            QQClient.PacketManager.SendPacket(packet, QQPort.Main.Name);
        }
        /// <summary>请求得到多人对话列表
        /// Gets the dialog list.
        /// </summary>
        public void GetDialogList()
        {
            ClusterSubClusterOpPacket packet = new ClusterSubClusterOpPacket(QQUser);
            packet.OpByte = _08QQ.SubCMD_Cluster.QQ_CLUSTER_SUB_CMD_GET_DIALOG_LIST;
            QQClient.PacketManager.SendPacket(packet, QQPort.Main.Name);
        }

        /// <summary>创建一个临时群
        /// Creates the temporary cluster.
        /// </summary>
        /// <param name="name">The name.群名称</param>
        /// <param name="type">The type.临时群类型</param>
        /// <param name="parentClusterId">The parent cluster id.父群内部ID</param>
        /// <param name="members">The members.成员QQ号数组</param>
        public void CreateTemporaryCluster(string name, _08QQ.ClusterType type, int parentClusterId, List<int> members)
        {
            ClusterCreateTempPacket packet = new ClusterCreateTempPacket(QQUser);
            packet.Type = type;
            packet.Name = name;
            packet.Members = members;
            packet.ParentClusterId = parentClusterId;
            QQClient.PacketManager.SendPacket(packet, QQPort.Main.Name);
        }
        /// <summary>创建一个固定群
        /// Creates the permanent cluster.
        /// </summary>
        /// <param name="name">The name.群名称</param>
        /// <param name="notice">The notice.群声明</param>
        /// <param name="desription">The desription.群描述</param>
        /// <param name="members">The members.群成员</param>
        /// <param name="category">The category.群的分类</param>
        /// <param name="authType">Type of the auth.群认证类型</param>
        public void CreatePermanentCluster(string name, string notice, string desription, List<int> members, int category, _08QQ.AuthType authType)
        {
            ClusterCreatePacket packet = new ClusterCreatePacket(QQUser);
            packet.Type = _08QQ.ClusterType.QQ_CLUSTER_TYPE_PERMANENT;
            packet.AuthType = authType;
            packet.Category = category;
            packet.Name = name;
            packet.Notice = notice;
            packet.Description = desription;
            packet.Members = members;
            QQClient.PacketManager.SendPacket(packet, QQPort.Main.Name);
        }
        #region events
        /// <summary>加入群成功
        /// Occurs when [cluster join Successfully].
        /// </summary>
        public event EventHandler<QQEventArgs<ClusterJoinPacket, ClusterCommandReplyPacket>> JoinClusterSuccessfully;
        internal void OnClusterJoinSuccessfully(QQEventArgs<ClusterJoinPacket, ClusterCommandReplyPacket> e)
        {
            if (JoinClusterSuccessfully != null)
            {
                JoinClusterSuccessfully(this.QQClient, e);
            }
        }
        /// <summary>加入群需要验证信息
        /// Occurs when [join cluster need auth].
        /// </summary>
        public event EventHandler<QQEventArgs<ClusterJoinPacket, ClusterCommandReplyPacket>> JoinClusterNeedAuth;
        /// <summary>
        /// Raises the <see cref="E:JoinClusterNeedAuth"/> event.
        /// </summary>
        /// <param name="e">The <see cref="LumaQQ.NET.Events.QQEventArgs&lt;LumaQQ.NET.Packets.In.ClusterCommandReplyPacket,LumaQQ.NET.Packets.Out.ClusterJoinPacket&gt;"/> instance containing the event data.</param>
        internal void OnJoinClusterNeedAuth(QQEventArgs<ClusterJoinPacket, ClusterCommandReplyPacket> e)
        {
            if (JoinClusterNeedAuth != null)
            {
                JoinClusterNeedAuth(this.QQClient, e);
            }
        }
        /// <summary>被别人拒绝加入群  (发送验证消息的结果返回事件请使用RejectJoinCluster事件)
        /// Occurs when [join cluster denied].
        /// </summary>
        public event EventHandler<QQEventArgs<ClusterJoinPacket, ClusterCommandReplyPacket>> JoinClusterDenied;
        /// <summary>
        /// Raises the <see cref="E:JoinClusterDenied"/> event.
        /// </summary>
        /// <param name="e">The <see cref="LumaQQ.NET.Events.QQEventArgs&lt;LumaQQ.NET.Packets.Out.ClusterCommandPacket,LumaQQ.NET.Packets.Out.ClusterJoinPacket&gt;"/> instance containing the event data.</param>
        internal void OnJoinClusterDenied(QQEventArgs<ClusterJoinPacket, ClusterCommandReplyPacket> e)
        {
            if (JoinClusterDenied != null)
            {
                JoinClusterDenied(this.QQClient, e);
            }
        }
        /// <summary>加入群失败
        /// Occurs when [join cluster failed].
        /// </summary>
        public event EventHandler<QQEventArgs<ClusterJoinPacket, ClusterCommandReplyPacket>> JoinClusterFailed;
        /// <summary>
        /// Raises the <see cref="E:JoinClusterFailed"/> event.
        /// </summary>
        /// <param name="e">The <see cref="LumaQQ.NET.Events.QQEventArgs&lt;LumaQQ.NET.Packets.In.ClusterCommandReplyPacket,LumaQQ.NET.Packets.Out.ClusterJoinPacket&gt;"/> instance containing the event data.</param>
        internal void OnJoinClusterFailed(QQEventArgs<ClusterJoinPacket, ClusterCommandReplyPacket> e)
        {
            if (JoinClusterFailed != null)
            {
                JoinClusterFailed(this.QQClient, e);
            }
        }

        /// <summary>发送加入群验证信息成功
        /// Occurs when [send join cluster auth Successfully].
        /// </summary>
        public event EventHandler<QQEventArgs<ClusterAuthPacket, ClusterCommandReplyPacket>> SendJoinClusterAuthSuccessfully;
        internal void OnSendJoinClusterAuthSuccessfully(QQEventArgs<ClusterAuthPacket, ClusterCommandReplyPacket> e)
        {
            if (SendJoinClusterAuthSuccessfully != null)
            {
                SendJoinClusterAuthSuccessfully(this.QQClient, e);
            }
        }
        /// <summary>发送加入群验证信息失败
        /// Occurs when [send join cluster auth failed].
        /// </summary>
        public event EventHandler<QQEventArgs<ClusterAuthPacket, ClusterCommandReplyPacket>> SendJoinClusterAuthFailed;
        /// <summary>
        /// Raises the <see cref="E:SendJoinClusterAuthFailed"/> event.
        /// </summary>
        /// <param name="e">The <see cref="LumaQQ.NET.Events.QQEventArgs&lt;LumaQQ.NET.Packets.In.ClusterCommandReplyPacket,LumaQQ.NET.Packets.Out.ClusterAuthPacket&gt;"/> instance containing the event data.</param>
        internal void OnSendJoinClusterAuthFailed(QQEventArgs<ClusterAuthPacket, ClusterCommandReplyPacket> e)
        {
            if (SendJoinClusterAuthFailed != null)
            {
                SendJoinClusterAuthFailed(this.QQClient, e);
            }
        }

        /// <summary>
        /// Occurs when [search cluster Successfully].
        /// </summary>
        public event EventHandler<QQEventArgs<ClusterSearchPacket, ClusterCommandReplyPacket>> SearchClusterSuccessfully;
        /// <summary>
        /// Raises the <see cref="E:SearchClusterSuccessfully"/> event.
        /// </summary>
        /// <param name="e">The <see cref="LumaQQ.NET.Events.QQEventArgs&lt;LumaQQ.NET.Packets.In.ClusterCommandReplyPacket,LumaQQ.NET.Packets.Out.ClusterSearchPacket&gt;"/> instance containing the event data.</param>
        internal void OnSearchClusterSuccessfully(QQEventArgs<ClusterSearchPacket, ClusterCommandReplyPacket> e)
        {
            if (SearchClusterSuccessfully != null)
            {
                SearchClusterSuccessfully(this.QQClient, e);
            }
        }
        /// <summary>
        /// Occurs when [search cluster failed].
        /// </summary>
        public event EventHandler<QQEventArgs<ClusterSearchPacket, ClusterCommandReplyPacket>> SearchClusterFailed;
        /// <summary>
        /// Raises the <see cref="E:SearchClusterFailed"/> event.
        /// </summary>
        /// <param name="e">The <see cref="LumaQQ.NET.Events.QQEventArgs&lt;LumaQQ.NET.Packets.In.ClusterCommandReplyPacket,LumaQQ.NET.Packets.Out.ClusterSearchPacket&gt;"/> instance containing the event data.</param>
        internal void OnSearchClusterFailed(QQEventArgs<ClusterSearchPacket, ClusterCommandReplyPacket> e)
        {
            if (SearchClusterFailed != null)
            {
                SearchClusterFailed(this.QQClient, e);
            }
        }

        /// <summary>
        /// Occurs when [exit cluster Successfully].
        /// </summary>
        public event EventHandler<QQEventArgs<ClusterExitPacket, ClusterCommandReplyPacket>> ExitClusterSuccessfully;
        /// <summary>
        /// Raises the <see cref="E:ExitClusterSuccessfully"/> event.
        /// </summary>
        /// <param name="e">The <see cref="LumaQQ.NET.Events.QQEventArgs&lt;LumaQQ.NET.Packets.In.ClusterCommandReplyPacket,LumaQQ.NET.Packets.Out.ClusterExitPacket&gt;"/> instance containing the event data.</param>
        internal void OnExitClusterSuccessfully(QQEventArgs<ClusterExitPacket, ClusterCommandReplyPacket> e)
        {
            if (ExitClusterSuccessfully != null)
            {
                ExitClusterSuccessfully(this.QQClient, e);
            }
        }
        /// <summary>
        /// Occurs when [exit cluster failed].
        /// </summary>
        public event EventHandler<QQEventArgs<ClusterExitPacket, ClusterCommandReplyPacket>> ExitClusterFailed;
        /// <summary>
        /// Raises the <see cref="E:ExitClusterFailed"/> event.
        /// </summary>
        /// <param name="e">The <see cref="LumaQQ.NET.Events.QQEventArgs&lt;LumaQQ.NET.Packets.In.ClusterCommandReplyPacket,LumaQQ.NET.Packets.Out.ClusterExitPacket&gt;"/> instance containing the event data.</param>
        internal void OnExitClusterFailed(QQEventArgs<ClusterExitPacket, ClusterCommandReplyPacket> e)
        {
            if (ExitClusterFailed != null)
            {
                ExitClusterFailed(this.QQClient, e);
            }
        }

        /// <summary>
        /// Occurs when [send cluster IM Successfully].
        /// </summary>
        public event EventHandler<QQEventArgs<ClusterSendIMExPacket, ClusterCommandReplyPacket>> SendClusterIMExSuccessfully;
        /// <summary>
        /// Raises the <see cref="E:SendClusterIMExSuccessfully"/> event.
        /// </summary>
        /// <param name="e">The <see cref="LumaQQ.NET.Events.QQEventArgs&lt;LumaQQ.NET.Packets.In.ClusterCommandReplyPacket,LumaQQ.NET.Packets.Out.ClusterSendIMExPacket&gt;"/> instance containing the event data.</param>
        internal void OnSendClusterIMExSuccessfully(QQEventArgs<ClusterSendIMExPacket, ClusterCommandReplyPacket> e)
        {
            if (SendClusterIMExSuccessfully != null)
            {
                SendClusterIMExSuccessfully(this.QQClient, e);
            }
        }

        /// <summary>
        /// Occurs when [send cluster IM failed].
        /// </summary>
        public event EventHandler<QQEventArgs<ClusterSendIMExPacket, ClusterCommandReplyPacket>> SendClusterIMExFailed;
        /// <summary>
        /// Raises the <see cref="E:SendClusterIMExFailed"/> event.
        /// </summary>
        /// <param name="e">The <see cref="LumaQQ.NET.Events.QQEventArgs&lt;LumaQQ.NET.Packets.In.ClusterCommandReplyPacket,LumaQQ.NET.Packets.Out.ClusterSendIMExPacket&gt;"/> instance containing the event data.</param>
        internal void OnSendClusterIMExFailed(QQEventArgs<ClusterSendIMExPacket, ClusterCommandReplyPacket> e)
        {
            if (SendClusterIMExFailed != null)
            {
                SendClusterIMExFailed(this.QQClient, e);
            }
        }
        /// <summary>
        /// 存放临时分片包
        /// </summary>
        Dictionary<int, Dictionary<int, byte[]>> fragments = new Dictionary<int, Dictionary<int, byte[]>>();
        /// <summary>接收到普通群消息
        /// 注意ReceiveIMPacket中的属性并不是都有用。
        /// 并且event args中的OutPacket始终为空
        /// Occurs when [receive cluster IM].
        /// </summary>
        public event EventHandler<QQEventArgs<ReceiveIMPacket>> ReceiveClusterIM;
        internal void OnReceiveClusterIM(QQEventArgs<ReceiveIMPacket> e)
        {
            if (e.Packet.ClusterIM.FragmentCount > 1)
            {
                if (!fragments.ContainsKey(e.Packet.ClusterIM.MessageId))
                {
                    fragments.Add(e.Packet.ClusterIM.MessageId, new Dictionary<int, byte[]>());
                }
                Dictionary<int, byte[]> messageFragments = fragments[e.Packet.ClusterIM.MessageId];
                if (!messageFragments.ContainsKey(e.Packet.ClusterIM.FragmentSequence))
                {
                    messageFragments.Add(e.Packet.ClusterIM.FragmentSequence, e.Packet.ClusterIM.MessageBytes);
                }
                //如果消息分片还没有接收完
                if (messageFragments.Count < e.Packet.ClusterIM.FragmentCount)
                {
                    return;
                }
                //已经接收到了所有分片包，要注意，包的接收顺序可能不是顺序接收到的。
                //合成包
                List<byte> messageBytes = new List<byte>();
                for (int i = 0; i < e.Packet.ClusterIM.FragmentCount; i++)
                {
                    messageBytes.AddRange(messageFragments[i]);
                }
                fragments.Remove(e.Packet.ClusterIM.MessageId);
                e.Packet.ClusterIM.MessageBytes = messageBytes.ToArray();
            }

            if (ReceiveClusterIM != null)
            {
                ReceiveClusterIM(this.QQClient, e);
            }
        }

        /// <summary>被加入到群
        /// Occurs when [added to cluster].
        /// </summary>
        public event EventHandler<QQEventArgs<ReceiveIMPacket>> AddedToCluster;
        /// <summary>
        /// Raises the <see cref="E:AddedToCluster"/> event.
        /// </summary>
        /// <param name="e">The <see cref="LumaQQ.NET.Events.QQEventArgs&lt;LumaQQ.NET.Packets.In.ReceiveIMPacket,LumaQQ.NET.Packets.OutPacket&gt;"/> instance containing the event data.</param>
        internal void OnAddedToCluster(QQEventArgs<ReceiveIMPacket> e)
        {
            if (AddedToCluster != null)
            {
                AddedToCluster(this.QQClient, e);
            }
        }

        /// <summary>从一个群中被移除
        /// Occurs when [removed from cluster].
        /// </summary>
        public event EventHandler<QQEventArgs<ReceiveIMPacket>> RemovedFromCluster;
        /// <summary>
        /// Raises the <see cref="E:RemovedFromCluster"/> event.
        /// </summary>
        /// <param name="e">The <see cref="LumaQQ.NET.Events.QQEventArgs&lt;LumaQQ.NET.Packets.In.ReceiveIMPacket,LumaQQ.NET.Packets.OutPacket&gt;"/> instance containing the event data.</param>
        internal void OnRemovedFromCluster(QQEventArgs<ReceiveIMPacket> e)
        {
            if (RemovedFromCluster != null)
            {
                RemovedFromCluster(this.QQClient, e);
            }
        }

        /// <summary>对方同意我加入群，带附加消息属性Packet.Message
        /// Occurs when [approved join cluster].
        /// </summary>
        public event EventHandler<QQEventArgs<ReceiveIMPacket>> ApprovedJoinCluster;
        /// <summary>
        /// Raises the <see cref="E:ApprovedJoinCluster"/> event.
        /// </summary>
        /// <param name="e">The <see cref="LumaQQ.NET.Events.QQEventArgs&lt;LumaQQ.NET.Packets.In.ReceiveIMPacket,LumaQQ.NET.Packets.OutPacket&gt;"/> instance containing the event data.</param>
        internal void OnApprovedJoinCluster(QQEventArgs<ReceiveIMPacket> e)
        {
            if (ApprovedJoinCluster != null)
            {
                ApprovedJoinCluster(this.QQClient, e);
            }
        }

        /// <summary>被拒绝加入群，带附加消息
        /// Occurs when [reject join cluster].
        /// </summary>
        public event EventHandler<QQEventArgs<ReceiveIMPacket>> RejectJoinCluster;
        /// <summary>
        /// Raises the <see cref="E:RejectJoinCluster"/> event.
        /// </summary>
        /// <param name="e">The <see cref="LumaQQ.NET.Events.QQEventArgs&lt;LumaQQ.NET.Packets.In.ReceiveIMPacket,LumaQQ.NET.Packets.OutPacket&gt;"/> instance containing the event data.</param>
        internal void OnRejectJoinCluster(QQEventArgs<ReceiveIMPacket> e)
        {
            if (RejectJoinCluster != null)
            {
                RejectJoinCluster(this.QQClient, e);
            }
        }

        /// <summary>有人请求加入群，带附加消息
        /// Occurs when [has requested join cluster].
        /// </summary>
        public event EventHandler<QQEventArgs<ReceiveIMPacket>> HasRequestedJoinCluster;
        /// <summary>
        /// Raises the <see cref="E:HasRequestedJoinCluster"/> event.
        /// </summary>
        /// <param name="e">The <see cref="LumaQQ.NET.Events.QQEventArgs&lt;LumaQQ.NET.Packets.In.ReceiveIMPacket,LumaQQ.NET.Packets.OutPacket&gt;"/> instance containing the event data.</param>
        internal void OnHasRequestJoinCluster(QQEventArgs<ReceiveIMPacket> e)
        {
            if (HasRequestedJoinCluster != null)
            {
                HasRequestedJoinCluster(this.QQClient, e);
            }
        }

        /// <summary>
        /// Occurs when [modify card Successfully].
        /// </summary>
        public event EventHandler<QQEventArgs<ClusterModifyCardPacket, ClusterCommandReplyPacket>> ModifyCardSuccessfully;
        /// <summary>
        /// Raises the <see cref="E:ModifyCardSuccssed"/> event.
        /// </summary>
        /// <param name="e">The <see cref="LumaQQ.NET.Events.QQEventArgs&lt;LumaQQ.NET.Packets.In.ClusterCommandReplyPacket,LumaQQ.NET.Packets.Out.ClusterModifyCardPacket&gt;"/> instance containing the event data.</param>
        internal void OnModifyCardSuccessfully(QQEventArgs<ClusterModifyCardPacket, ClusterCommandReplyPacket> e)
        {
            if (ModifyCardSuccessfully != null)
            {
                ModifyCardSuccessfully(this.QQClient, e);
            }
        }

        /// <summary>
        /// Occurs when [modify card failed].
        /// </summary>
        public event EventHandler<QQEventArgs<ClusterModifyCardPacket, ClusterCommandReplyPacket>> ModifyCardFailed;
        /// <summary>
        /// Raises the <see cref="E:ModifyCardFailed"/> event.
        /// </summary>
        /// <param name="e">The <see cref="LumaQQ.NET.Events.QQEventArgs&lt;LumaQQ.NET.Packets.In.ClusterCommandReplyPacket,LumaQQ.NET.Packets.Out.ClusterModifyCardPacket&gt;"/> instance containing the event data.</param>
        internal void OnModifyCardFailed(QQEventArgs<ClusterModifyCardPacket, ClusterCommandReplyPacket> e)
        {
            if (ModifyCardFailed != null)
            {
                ModifyCardFailed(this.QQClient, e);
            }
        }

        /// <summary>
        /// Occurs when [update organization Successfully].
        /// </summary>
        public event EventHandler<QQEventArgs<ClusterUpdateOrganizationPacket, ClusterCommandReplyPacket>> UpdateOrganizationSuccessfully;
        /// <summary>
        /// Raises the <see cref="E:UpdateOrganizationSuccessfully"/> event.
        /// </summary>
        /// <param name="e">The <see cref="LumaQQ.NET.Events.QQEventArgs&lt;LumaQQ.NET.ClusterCommand,LumaQQ.NET.Packets.Out.ClusterUpdateOrganizationPacket&gt;"/> instance containing the event data.</param>
        internal void OnUpdateOrganizationSuccessfully(QQEventArgs<ClusterUpdateOrganizationPacket, ClusterCommandReplyPacket> e)
        {
            if (UpdateOrganizationSuccessfully != null)
            {
                UpdateOrganizationSuccessfully(this.QQClient, e);
            }
        }

        /// <summary>
        /// Occurs when [update organization failed].
        /// </summary>
        public event EventHandler<QQEventArgs<ClusterUpdateOrganizationPacket, ClusterCommandReplyPacket>> UpdateOrganizationFailed;
        /// <summary>
        /// Raises the <see cref="E:UpdateOrganizationFailed"/> event.
        /// </summary>
        /// <param name="e">The <see cref="LumaQQ.NET.Events.QQEventArgs&lt;LumaQQ.NET.Packets.In.ClusterCommandReplyPacket,LumaQQ.NET.Packets.Out.ClusterUpdateOrganizationPacket&gt;"/> instance containing the event data.</param>
        internal void OnUpdateOrganizationFailed(QQEventArgs<ClusterUpdateOrganizationPacket, ClusterCommandReplyPacket> e)
        {
            if (UpdateOrganizationFailed != null)
            {
                UpdateOrganizationFailed(this.QQClient, e);
            }
        }

        /// <summary>
        /// Occurs when [get online member Successfully].
        /// </summary>
        public event EventHandler<QQEventArgs<ClusterGetOnlineMemberPacket, ClusterCommandReplyPacket>> GetOnlineMemberSuccessfully;
        /// <summary>
        /// Raises the <see cref="E:GetOnlineMemberSuccessfully"/> event.
        /// </summary>
        /// <param name="e">The <see cref="LumaQQ.NET.Events.QQEventArgs&lt;LumaQQ.NET.Packets.In.ClusterCommandReplyPacket,LumaQQ.NET.Packets.Out.ClusterGetOnlineMemberPacket&gt;"/> instance containing the event data.</param>
        internal void OnGetOnlineMemberSuccessfully(QQEventArgs<ClusterGetOnlineMemberPacket, ClusterCommandReplyPacket> e)
        {
            if (GetOnlineMemberSuccessfully != null)
            {
                GetOnlineMemberSuccessfully(this.QQClient, e);
            }
        }

        /// <summary>
        /// Occurs when [get online member failed].
        /// </summary>
        public event EventHandler<QQEventArgs<ClusterGetOnlineMemberPacket, ClusterCommandReplyPacket>> GetOnlineMemberFailed;
        /// <summary>
        /// Raises the <see cref="E:GetOnlineMemberFailed"/> event.
        /// </summary>
        /// <param name="e">The <see cref="LumaQQ.NET.Events.QQEventArgs&lt;LumaQQ.NET.Packets.In.ClusterCommandReplyPacket,LumaQQ.NET.Packets.Out.ClusterGetOnlineMemberPacket&gt;"/> instance containing the event data.</param>
        internal void OnGetOnlineMemberFailed(QQEventArgs<ClusterGetOnlineMemberPacket, ClusterCommandReplyPacket> e)
        {
            if (GetOnlineMemberFailed != null)
            {
                GetOnlineMemberSuccessfully(this.QQClient, e);
            }
        }

        /// <summary>
        /// Occurs when [get cluster info Successfully].
        /// </summary>
        public event EventHandler<QQEventArgs<ClusterGetInfoPacket, ClusterCommandReplyPacket>> GetClusterInfoSuccessfully;
        /// <summary>
        /// Raises the <see cref="E:GetClusterInfoSuccessfully"/> event.
        /// </summary>
        /// <param name="e">The <see cref="LumaQQ.NET.Events.QQEventArgs&lt;LumaQQ.NET.Packets.In.ClusterCommandReplyPacket,LumaQQ.NET.Packets.Out.ClusterGetInfoPacket&gt;"/> instance containing the event data.</param>
        internal void OnGetClusterInfoSuccessfully(QQEventArgs<ClusterGetInfoPacket, ClusterCommandReplyPacket> e)
        {
            if (GetClusterInfoSuccessfully != null)
            {
                GetClusterInfoSuccessfully(this.QQClient, e);
            }
        }

        /// <summary>
        /// Occurs when [get cluster info failed].
        /// </summary>
        public event EventHandler<QQEventArgs<ClusterGetInfoPacket, ClusterCommandReplyPacket>> GetClusterInfoFailed;
        /// <summary>
        /// Raises the <see cref="E:GetClusterInfoFailed"/> event.
        /// </summary>
        /// <param name="e">The <see cref="LumaQQ.NET.Events.QQEventArgs&lt;LumaQQ.NET.Packets.In.ClusterCommandReplyPacket,LumaQQ.NET.Packets.Out.ClusterGetInfoPacket&gt;"/> instance containing the event data.</param>
        internal void OnGetClusterInfoFailed(QQEventArgs<ClusterGetInfoPacket, ClusterCommandReplyPacket> e)
        {
            if (GetClusterInfoFailed != null)
            {
                GetClusterInfoFailed(this.QQClient, e);
            }
        }

        /// <summary>
        /// Occurs when [ge member info Successfully].
        /// </summary>
        public event EventHandler<QQEventArgs<ClusterGetMemberInfoPacket, ClusterCommandReplyPacket>> GetMemberInfoSuccessfully;
        /// <summary>
        /// Raises the <see cref="E:GetMemberInfoSuccessfully"/> event.
        /// </summary>
        /// <param name="e">The <see cref="LumaQQ.NET.Events.QQEventArgs&lt;LumaQQ.NET.Packets.In.ClusterCommandReplyPacket,LumaQQ.NET.Packets.Out.ClusterGetMemberInfoPacket&gt;"/> instance containing the event data.</param>
        internal void OnGetMemberInfoSuccessfully(QQEventArgs<ClusterGetMemberInfoPacket, ClusterCommandReplyPacket> e)
        {
            if (GetMemberInfoSuccessfully != null)
            {
                GetMemberInfoSuccessfully(this.QQClient, e);
            }
        }

        /// <summary>
        /// Occurs when [get member info failed].
        /// </summary>
        public event EventHandler<QQEventArgs<ClusterGetMemberInfoPacket, ClusterCommandReplyPacket>> GetMemberInfoFailed;
        /// <summary>
        /// Raises the <see cref="E:GetMemberInfoFailed"/> event.
        /// </summary>
        /// <param name="e">The <see cref="LumaQQ.NET.Events.QQEventArgs&lt;LumaQQ.NET.Packets.In.ClusterCommandReplyPacket,LumaQQ.NET.Packets.Out.ClusterGetMemberInfoPacket&gt;"/> instance containing the event data.</param>
        internal void OnGetMemberInfoFailed(QQEventArgs<ClusterGetMemberInfoPacket, ClusterCommandReplyPacket> e)
        {
            if (GetMemberInfoFailed != null)
            {
                GetMemberInfoFailed(this.QQClient, e);
            }
        }

        /// <summary>
        /// Occurs when [get card Successfully].
        /// </summary>
        public event EventHandler<QQEventArgs<ClusterGetCardPacket, ClusterCommandReplyPacket>> GetCardSuccessfully;
        /// <summary>
        /// Raises the <see cref="E:GetCardSuccessfully"/> event.
        /// </summary>
        /// <param name="e">The <see cref="LumaQQ.NET.Events.QQEventArgs&lt;LumaQQ.NET.Packets.In.ClusterCommandReplyPacket,LumaQQ.NET.Packets.Out.ClusterGetCardPacket&gt;"/> instance containing the event data.</param>
        internal void OnGetCardSuccessfully(QQEventArgs<ClusterGetCardPacket, ClusterCommandReplyPacket> e)
        {
            if (GetCardSuccessfully != null)
            {
                GetCardSuccessfully(this.QQClient, e);
            }
        }

        /// <summary>
        /// Occurs when [get card failed].
        /// </summary>
        public event EventHandler<QQEventArgs<ClusterGetCardPacket, ClusterCommandReplyPacket>> GetCardFailed;
        /// <summary>
        /// Raises the <see cref="E:GetCardFailed"/> event.
        /// </summary>
        /// <param name="e">The <see cref="LumaQQ.NET.Events.QQEventArgs&lt;LumaQQ.NET.Packets.In.ClusterCommandReplyPacket,LumaQQ.NET.Packets.Out.ClusterGetCardPacket&gt;"/> instance containing the event data.</param>
        internal void OnGetCardFailed(QQEventArgs<ClusterGetCardPacket, ClusterCommandReplyPacket> e)
        {
            if (GetCardFailed != null)
            {
                GetCardFailed(this.QQClient, e);
            }
        }

        /// <summary>
        /// Occurs when [batch get card Successfully].
        /// </summary>
        public event EventHandler<QQEventArgs<ClusterGetCardBatchPacket, ClusterCommandReplyPacket>> BatchGetCardSuccessfully;
        /// <summary>
        /// Raises the <see cref="E:BatchGetCardSuccessfully"/> event.
        /// </summary>
        /// <param name="e">The <see cref="LumaQQ.NET.Events.QQEventArgs&lt;LumaQQ.NET.Packets.In.ClusterCommandReplyPacket,LumaQQ.NET.Packets.Out.ClusterGetCardBatchPacket&gt;"/> instance containing the event data.</param>
        internal void OnBatchGetCardSuccessfully(QQEventArgs<ClusterGetCardBatchPacket, ClusterCommandReplyPacket> e)
        {
            if (BatchGetCardSuccessfully != null)
            {
                BatchGetCardSuccessfully(this.QQClient, e);
            }
        }

        /// <summary>
        /// Occurs when [batch get card failed].
        /// </summary>
        public event EventHandler<QQEventArgs<ClusterGetCardBatchPacket, ClusterCommandReplyPacket>> BatchGetCardFailed;
        /// <summary>
        /// Raises the <see cref="E:BatchGetCardFailed"/> event.
        /// </summary>
        /// <param name="e">The <see cref="LumaQQ.NET.Events.QQEventArgs&lt;LumaQQ.NET.Packets.In.ClusterCommandReplyPacket,LumaQQ.NET.Packets.Out.ClusterGetCardBatchPacket&gt;"/> instance containing the event data.</param>
        internal void OnBatchGetCardFailed(QQEventArgs<ClusterGetCardBatchPacket, ClusterCommandReplyPacket> e)
        {
            if (BatchGetCardFailed != null)
            {
                BatchGetCardFailed(this.QQClient, e);
            }
        }

        /// <summary>
        /// Occurs when [get dialog subject Successfully].
        /// <remarks>获取对话列表(GetDialog)和获取讨论组(GetSubject)公用事件</remarks>
        /// </summary>
        public event EventHandler<QQEventArgs<ClusterSubClusterOpPacket, ClusterCommandReplyPacket>> GetDialogSubjectSuccessfully;
        /// <summary>
        /// Raises the <see cref="E:GetDialogSubjectSuccessfully"/> event.
        /// </summary>
        /// <param name="e">The <see cref="LumaQQ.NET.Events.QQEventArgs&lt;LumaQQ.NET.Packets.In.ClusterCommandReplyPacket,LumaQQ.NET.Packets.Out.ClusterSubClusterOpPacket&gt;"/> instance containing the event data.</param>
        internal void OnGetDialogSubjectSuccessfully(QQEventArgs<ClusterSubClusterOpPacket, ClusterCommandReplyPacket> e)
        {
            if (GetDialogSubjectSuccessfully != null)
            {
                GetDialogSubjectSuccessfully(this.QQClient, e);
            }
        }

        /// <summary>
        /// Occurs when [get dialog subject failed].
        /// <remarks>获取对话列表(GetDialog)和获取讨论组(GetSubject)公用事件</remarks>
        /// </summary>
        public event EventHandler<QQEventArgs<ClusterSubClusterOpPacket, ClusterCommandReplyPacket>> GetDialogSubjectFailed;
        /// <summary>
        /// Raises the <see cref="E:GetDialogSubjectFailed"/> event.
        /// </summary>
        /// <param name="e">The <see cref="LumaQQ.NET.Events.QQEventArgs&lt;LumaQQ.NET.Packets.In.ClusterCommandReplyPacket,LumaQQ.NET.Packets.Out.ClusterSubClusterOpPacket&gt;"/> instance containing the event data.</param>
        internal void OnGetDialogSubjectFailed(QQEventArgs<ClusterSubClusterOpPacket, ClusterCommandReplyPacket> e)
        {
            if (GetDialogSubjectFailed != null)
            {
                GetDialogSubjectFailed(this.QQClient, e);
            }
        }
        #endregion
    }
}
